package com.kanesoft.android.gdx.control.input;

import java.util.Iterator;
import java.util.Vector;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.math.Vector2;
import com.kanesoft.android.gdx.widgets.sprites.SpriteInfo;
import com.kanesoft.android.gdx.widgets.sprites.SpriteSlideInfo;
import com.kanesoft.android.gdx.widgets.sprites.SpriteWidget;

public class TouchGesturesController {

    public enum GestureAngle {
        DEG_0, DEG_45, DEG_90, DEG_135, DEG_180, DEG_225, DEG_270, DEG_315
    };

    private final int MAX_GESTURE_POINTS = 10;
    private final float MINIMUM_INTENSITY = 30;
    private final float MAX_WAIT_TIME = 0.100f;
    private int lastGesturePointIndex;

    private TouchVectors[] gesturesPoints;
    private float lastDeltaTime;

    private Vector<TouchListType> listOfGestures;
    private TouchEventsCallBack touchCallBack;

    private SpriteWidget[] arrow;
    private SpriteInfo arrowSpriteInfo;
    private final float ARROW_WIDTH = 512;
    private final float ARROW_HEIGHT = 124;
    private final int MAX_ARROWS = 5;
    private float arrow_scale_factor_width;
    private float arrow_scale_factor_height;
    private int current_arrow;

    private Vector2 lastPoint;
    private TouchVectors actualGestureDirection;
    private final float MAX_MATCHING_ANGLE = 30f;

    private class TouchListType {
        TouchVectors[] vectorObject;
        Object userObject;

        TouchListType(TouchVectors[] vectors, Object objects) {
            vectorObject = vectors;
            userObject = objects;

            arrowSpriteInfo = new SpriteInfo("data/screen_arrow_red.png", 0.150f);
            arrowSpriteInfo.addSpriteSlide(new SpriteSlideInfo(0, 0, (int) ARROW_WIDTH, (int) ARROW_HEIGHT));
            arrow_scale_factor_width = Gdx.graphics.getWidth() / ARROW_WIDTH;
            arrow_scale_factor_height = Gdx.graphics.getHeight() / ARROW_HEIGHT;

            arrow = new SpriteWidget[MAX_ARROWS];

            for (int i = 0; i < arrow.length; i++) {
                arrow[i] = new SpriteWidget(0, 0, 0, 0, arrowSpriteInfo, null);
                arrow[i].enabled = false;
            }
        }
    }

    public TouchGesturesController(TouchEventsCallBack touchCallBack) {
        gesturesPoints = new TouchVectors[MAX_GESTURE_POINTS];
        listOfGestures = new Vector<TouchListType>();
        this.touchCallBack = touchCallBack;
    }

    public void addMovement(float x, float y, float deltaTime) {
        Vector2 actualPoint = new Vector2(x, y);

        if (lastPoint == null) {
            lastPoint = actualPoint;
            resetArrows(x, y);
            return;
        }

        TouchVectors actualGesture = new TouchVectors(lastPoint, actualPoint);
        lastPoint = actualPoint;

        if (actualGestureDirection == null) {
            actualGestureDirection = actualGesture;
            return;
        }

        updateArrow(actualGestureDirection.angleInDegrees, actualGestureDirection.intensity);

        if ((Math.abs(actualGestureDirection.angleInDegrees - actualGesture.angleInDegrees) < MAX_MATCHING_ANGLE)) {
            actualGestureDirection.intensity += actualGesture.intensity;
            return;
        }

        addGesturePoint(actualGestureDirection);

        actualGestureDirection = null;
        lastPoint = null;

        incrementArrow();
    }

  
    private void incrementArrow() {
        current_arrow++;
        if (current_arrow >= arrow.length) {
            current_arrow = 0;
            arrow[current_arrow].enabled = false;
        }
    }

    private void resetArrows(float x, float y) {
        arrow[current_arrow].enabled = false;
        arrow[current_arrow].setRotationAngle(0);
        arrow[current_arrow].setSpritePosition(x, Gdx.graphics.getHeight() - y);
        arrow[current_arrow].setSpriteSize(0, 0);
    }

    private void updateArrow(float angle, float intensity) {
        arrow[current_arrow].enabled = true;
        arrow[current_arrow].setRotationAngle(angle);
        arrow[current_arrow].setSpriteSize((arrow_scale_factor_width * intensity) / Gdx.graphics.getWidth(),
                (arrow_scale_factor_height * 10) / Gdx.graphics.getHeight());
    }

    public void finalizeMovement(int x, int y) {
        System.out.println("FINALIZE...");
        addMovement(x, y, lastDeltaTime + MAX_WAIT_TIME);
        analyseGestures();

        for (int i = 0; i < gesturesPoints.length; i++) {
            gesturesPoints[i] = null;
        }
        lastGesturePointIndex = 0;

        lastPoint = null;
        actualGestureDirection = null;
    }

    public int addGestureHandler(TouchVectors gesture, Object userData) {
        TouchVectors[] gestures = { gesture };

        listOfGestures.add(new TouchListType(gestures, userData));
        return listOfGestures.size();
    }

    public int addGestureHandler(TouchVectors[] gestures, Object userData) {
        listOfGestures.add(new TouchListType(gestures, userData));
        return listOfGestures.size();
    }

    private void analyseGestures() {
        for (Iterator<TouchListType> i = listOfGestures.iterator(); i.hasNext();) {

            TouchListType touchList = (TouchListType) i.next();
            TouchVectors[] gestures = touchList.vectorObject;

            if (gestures == null) continue;
            if (gestures.length > MAX_GESTURE_POINTS) continue;

            for (int j = 0; j < gestures.length; j++) {
                if (gestures[j] == null) break;
                if (gesturesPoints[j] == null) break;

                if (gestures[j].angle != gesturesPoints[j].angle) break;
                if (gestures[j].intensity > gesturesPoints[j].intensity) break;

                if (j == (gestures.length - 1)) {
                    touchCallBack.gestureExecuted(touchList.userObject);
                }
            }
        }
    }

    
    private float drawTime;
    public void drawArrows(float deltaTime) {
        
        drawTime += deltaTime;
        
        for (int i = 0; i < arrow.length; i++) {

            if ((arrow[i].enabled) && (arrow[i].userTag == 0)) {
                arrow[i].userTag = drawTime;
            }
            
            if ((drawTime - arrow[i].userTag ) > 0.5f) {
                arrow[i].enabled = false;
                arrow[i].userTag = 0;
                arrow[i].setSpriteSize(0, 0);
            }
            
            arrow[i].draw(deltaTime, 0, 0);
        }
    }

    private void addGesturePoint(TouchVectors point) {
        if (point == null) return;
        if (point.intensity < MINIMUM_INTENSITY) return;
        if ((lastGesturePointIndex > 0) && (point.angle == gesturesPoints[lastGesturePointIndex - 1].angle)) return;

        if (lastGesturePointIndex >= MAX_GESTURE_POINTS) {
            TouchVectors[] shiftPoints = new TouchVectors[MAX_GESTURE_POINTS];
            System.arraycopy(gesturesPoints, 1, shiftPoints, 0, MAX_GESTURE_POINTS - 1);
            gesturesPoints = shiftPoints;
            lastGesturePointIndex = MAX_GESTURE_POINTS - 1;
        }

        gesturesPoints[lastGesturePointIndex] = point;
        lastGesturePointIndex++;

        analyseGestures();
    }
}
